<!DOCTYPE html>
<html>
<!--
Copyright 2011 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<head>
<title>DOM MutationLog Tests</title>
<script src="../third_party/closure/closure/goog/base.js"></script>
<script src="compat.js"></script>
<script src="weak_map.js"></script>
<script src="forwarding_handler.js"></script>
<script src="observers.js"></script>
<script src="dom_mutation.js"></script>
<script>
goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>

function assertNextMutationIs(expected, log) {
  var mutation = log.shift();
  if (!expected) {
    assertUndefined('mutation exists', mutation);
    return;
  }

  assertTrue('mutation does not exist', !!mutation);

  var count = 0;
  for (prop in expected) {
    if (Array.isArray(expected[prop]) && Array.isArray(mutation[prop])) {
      assertArrayEquals('mutation prop ' + prop,
                        expected[prop], mutation[prop]);
    } else {
      assertEquals('mutation prop ' + prop, expected[prop], mutation[prop]);
    }
    count++;
  }

  assertEquals(count, Object.keys(mutation).length);
}

function browserSupportsAttributes() {
  var el = document.createElement('div');
  var sendsAttributeEvents = false;
  function attributeEventDetector(event) {
    sendsAttributeEvents = true;
  }
  el.addEventListener('DOMAttrModified', attributeEventDetector, false);
  el.setAttribute('id', 'foo');
  return sendsAttributeEvents;
}

function testAttributeChanged() {
  if (!browserSupportsAttributes()) return;

  var div = document.createElement('div');
  var span = div.appendChild(document.createElement('span'));
  var b = span.appendChild(document.createElement('b'));

  var log;
  var callback = function(mutations) {
    log = mutations;
  };
  var divLog;
  var divCallback = function(mutations) {
    divLog = mutations;
  };
  div.addSubtreeAttributeChangedListener(divCallback);
  b.addAttributeChangedListener(callback);
  // Should suppress this notification since it's the same log as above.
  b.addSubtreeAttributeChangedListener(callback);
  b.setAttribute('id', 'foo');

  window.notifyObservers_();
  assertEquals(1, log.length);
  assertNextMutationIs({
    type: 'AttributeChanged',
    target: b,
    attrName: 'id',
  }, log);
  assertEquals(1, divLog.length);
  assertNextMutationIs({
    type: 'AttributeChanged',
    target: b,
    attrName: 'id',
  }, divLog);

  b.removeAttributeChangedListener(callback);
  b.removeSubtreeAttributeChangedListener(callback);
  b.id = 'notobserved';

  window.notifyObservers_();
  assertEquals(0, log.length);
}

function testChildlistChanged() {
  var div = document.createElement('div');
  var span = document.createElement('span');
  var b = document.createElement('b');
  document.body.appendChild(div);  // required to pass in WebKit

  var log;
  var callback = function(mutations) {
    log = mutations;
  };
  var divLog;
  var divCallback = function(mutations) {
    divLog = mutations;
  };
  div.addSubtreeChangedListener(divCallback);
  span.addChildlistChangedListener(callback);
  div.appendChild(span);

  window.notifyObservers_();
  assertEquals(1, divLog.length);
  assertNextMutationIs({
    type: 'ChildlistChanged',
    target: div,
    added: [span],
    removed: []
  }, divLog);

  span.appendChild(b);

  window.notifyObservers_();
  assertEquals(1, log.length);
  assertNextMutationIs({
    type: 'ChildlistChanged',
    target: span,
    added: [b],
    removed: [],
  }, log);
  assertEquals(1, divLog.length);
  assertNextMutationIs({
    type: 'ChildlistChanged',
    target: span,
    added: [b],
    removed: [],
  }, divLog);

  div.removeSubtreeChangedListener(divCallback);
  div.appendChild(document.createElement('p'));

  window.notifyObservers_();
  assertEquals(0, divLog.length);
}

function testTextDataChanged() {
  var div = document.createElement('div');
  var span = div.appendChild(document.createElement('span'));
  span.innerHTML = 'Hello, world';
  var text = span.childNodes[0];

  var log;
  var callback = function(mutations) {
    log = mutations;
  };
  div.addTextDataChangedListener(callback);
  text.addTextDataChangedListener(callback);
  text.textContent = 'Goodbye, world';

  window.notifyObservers_();
  assertEquals(1, log.length);
  assertNextMutationIs({
    type: 'TextDataChanged',
    target: text
  }, log);

  div.removeTextDataChangedListener(callback);
  text.removeTextDataChangedListener(callback);
  text.textContent = 'Not observed';

  window.notifyObservers_();
  assertEquals(0, log.length);
}

</script>
</body>
</html>
